﻿using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using SlamCms.Common;

namespace SlamCms.Web.Mvc
{
	public enum AuthorizeTo
	{
		All,
		Authenticated
	}

	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
	public class AuthorizeToAttribute : ActionFilterAttribute, IAuthorizationFilter
	{
		static AuthorizeToAttribute()
		{
			LoginRouteValues = new RouteValueDictionary(new { action = "Login", controller = "Membership", area = "" });
		}

		public AuthorizeToAttribute(AuthorizeTo authorizeTo)
		{
			AuthorizeTo = authorizeTo;
		}

		public AuthorizeToAttribute(params string[] groups)
		{
			AuthorizeTo = AuthorizeTo.Authenticated;
			AuthorizedGroups = groups;
		}

		public AuthorizeTo AuthorizeTo { get; set; }
		public string[] AuthorizedGroups { get; set; }

		public static RouteValueDictionary LoginRouteValues { get; set; }

		public virtual void OnAuthorization(AuthorizationContext filterContext)
		{
			if (filterContext.IsChildAction || AuthorizeTo == AuthorizeTo.All)
				return;

			if (filterContext.HttpContext.User.Identity.IsAuthenticated)
			{
				// TODO add better validation for local usage
				if (filterContext.HttpContext.Request.GetRemoteAddress().IsPrivateIp())
					return;

				if (AuthorizedGroups == null || !AuthorizedGroups.Any())
					return;

				var slamCmsContext = DependencyResolver.Current.GetService<SlamCmsContext>();

				// check the user belongs to an allowed group
				if (slamCmsContext.User.Groups.Any(g => g.Name.In(AuthorizedGroups)))
					return;
			}

			// Redirect to Login
			var requestUrl = filterContext.HttpContext.Request.Url;

			//HACK: When MVC can't match the URL with any valid route, and the user is not authenticated, the original request URL is placed in the QueryString.
			var returnUrl = requestUrl.Query.StartsWith("?404;") ? requestUrl.Query.Substring(5) : requestUrl.ToString();

			var routeValues = new RouteValueDictionary(LoginRouteValues);
			routeValues.Add("returnUrl", returnUrl);
			var actionName = routeValues["action"].ToString();
			var controllerName = routeValues["controller"].ToString();

			var urlHelper = new UrlHelper(filterContext.RequestContext);
			var url = urlHelper.Action(actionName, controllerName, routeValues);

			if (url.IsNullOrEmpty())
			{
				var formsAuthentication = DependencyResolver.Current.GetService<IFormsAuthentication>();
				url = formsAuthentication.LoginUrl;
			}

			if (url.IsNullOrEmpty())
				throw new Exception("Cannot resolve login URL");

			filterContext.Result = new RedirectResult(url);
		}
	}
}